package com.semeureka.fault.mina;

import java.util.Arrays;
import java.util.Date;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoderAdapter;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.semeureka.fault.bean.Current;
import com.semeureka.fault.bean.Device;
import com.semeureka.fault.bean.Group;
import com.semeureka.fault.bean.Rawdata;
import com.semeureka.fault.bean.Voltage;
import com.semeureka.fault.bean.Warn;
import com.semeureka.fault.bean.Warn.WarnType;
import com.semeureka.fault.service.DeviceService;
import com.semeureka.fault.service.GroupService;
import com.semeureka.frame.util.HexUtils;

@Component
public class GprsMessageDecoder extends MessageDecoderAdapter {
	private static final Logger logger = LoggerFactory.getLogger(GprsMessageDecoder.class);
	@Autowired
	private DeviceService deviceService;
	@Autowired
	private GroupService groupService;
	@Autowired
	private SessionCache sessionCache;

	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		int oldPos = in.position();
		if (in.getUnsigned() == 0x68) { // 1、起始标志
			if (in.remaining() < 10) { // 2、长度位置
				return NEED_DATA;
			}
			if (in.skip(6).getUnsigned() != 0x68) { // 第2个标志位
				return NOT_OK;
			}
			int len = in.skip(1).getUnsignedShort();
			if (in.remaining() < len + 2) { // 3、报文长度
				return NEED_DATA;
			}
			int crc = HexUtils.sumupCrc(in, oldPos, len + 11);
			if (crc != in.skip(len).getUnsigned()) { // 4、加和校验
				return NOT_OK;
			}
			return in.getUnsigned() == 0x16 ? OK : NOT_OK; // 5、结束标志
		}
		return NOT_OK;
	}

	public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		byte[] content = new byte[in.getUnsignedShort(in.position() + 9) + 13];
		in.get(content);
		byte[] hostCode = Arrays.copyOfRange(content, 1, 7);
		Group group = groupService.findByHostCode(hostCode);
		Rawdata rawdata = new Rawdata(content, 0, group, new Date());
		out.write(rawdata);
		if (group != null) {
			sessionCache.put(group, session);
			decodeRawdata(session, rawdata, out);
		} else {
			logger.warn("接收到未登记主机0x{}的数据：{}", HexUtils.toHex(hostCode), HexUtils.toHex(content));
		}
		return OK;
	}

	private void decodeRawdata(IoSession session, Rawdata rawdata, ProtocolDecoderOutput out) throws Exception {
		IoBuffer in = IoBuffer.wrap(rawdata.getContent());
		in.skip(23);
		Rawdata response = null;
		while (in.remaining() >= 5) {
			byte[] code = new byte[2];
			in.get(code);
			Device device = deviceService.findByCode(code);
			int type = in.getUnsigned();
			switch (type) {
			case 0xDF:
				in.skip(18);
				out.write(new Warn(WarnType.SHORT, device, rawdata.getCreateTime()));
				break;
			case 0x8F:
				in.skip(18);
				out.write(new Warn(WarnType.EARTH, device, rawdata.getCreateTime()));
				break;
			case 0xDB: {
				Integer value = in.skip(2).getUnsignedShort();
				Integer temperture = in.getUnsignedShort();
				in.skip(12);
				out.write(new Current(value, temperture, device, rawdata.getCreateTime()));
				break;
			}
			case 0x90: {
				Integer value = 7; // 避免8个电流值较小时均值为0的情况
				for (int i = 0; i < 8; i++) {
					value += in.getUnsignedShort();
				}
				value = value / 8;
				Integer temperture = in.getUnsignedShort();
				out.write(new Current(value, temperture, device, rawdata.getCreateTime()));
				response = response != null ? response : new Rawdata(0x83, rawdata.getGroup());
				break;
			}
			case 0xDA: {
				Integer value = in.getUnsignedShort();
				out.write(new Voltage(value, device, rawdata.getCreateTime()));
				response = response != null ? response : new Rawdata(0x83, rawdata.getGroup());
				break;
			}
			case 0xA1:
			case 0xA0:
				in.skip(18); // 忽略
				break;
			default:
				logger.warn("无效的数据类型：0x{}，无法判断数据格式，跳过后续数据", String.format("%02X", type));
				in.skip(in.remaining());
				break;
			}
		}
		if (response != null) {
			session.write(response);
		}
	}
}
